package com.gz.springmvc.context;

import com.gz.springmvc.annotation.AutoWired;
import com.gz.springmvc.annotation.Controller;
import com.gz.springmvc.annotation.Service;
import com.gz.springmvc.xml.XMLParse;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Handler;

public class GzWebApplicationContext {
    //定义属性classFullPathList,保存扫描包/子包类的全路径
    private List<String> classFullPathList=
            new ArrayList<>();

    public ConcurrentHashMap<String, Object> ioc=new ConcurrentHashMap<>();

    private String configLocation; //spring容器配置文件

    //无参构造器
    public GzWebApplicationContext(){}

    public GzWebApplicationContext(String configLocation) {
        this.configLocation = configLocation;
    }

    public void init(){
        String basePackage = XMLParse.getBasePackage(configLocation);
        String[] split = basePackage.split(",");
        if(split.length>0){
            for (String s : split) {
                System.out.println(s);
                scanPackage(s);
            }
        }
        System.out.println("classFullPathList= "+classFullPathList);
        executeInstance();//将有注解的类注入ioc容器
        System.out.println("ioc= "+ioc);
        //完成注入后的类的对象的属性/字段的装配
        executeAutoWired();
        System.out.println("属性装配后的ioc= "+ioc);
    }

    //创建方法，完成对包的扫描
    //packageName表示要扫描的包，比如com.gz.controller
    public void scanPackage(String packageName) {

        //得到包所在的工作路径[绝对路径]
        //com.gz.controller -> com/gz/controller->
        // url=/E:/Java/java_code/framwork/gz-springmvc/target/gz-springmvc/WEB-INF/classes/com/gz/controller/
        URL url = this.getClass().getClassLoader().
                getResource("/"+packageName.replaceAll("\\.","/"));
//        System.out.println("url= "+url);
        String path = url.getFile();
        System.out.println("path= "+path);
        File file = new File(path);
        File[] files = file.listFiles();
        for (File file1 : files) {
            if (file1.isDirectory()) {//如果是目录，需要递归扫描
                scanPackage(packageName+"."+file1.getName());
            }else {
                //com.gz.controller.MonsterController.class
                String classFullPath=packageName+"."+file1.getName().replaceAll(".class","");
                classFullPathList.add(classFullPath);
            }
        }
    }

    //编写方法，将扫描到的类，在满足条件的情况下，反射到ioc容器
    public void executeInstance(){
        if(classFullPathList.size()==0){
            return;
        }

        try {
            for (String classFullPath : classFullPathList) {
                //反射
                Class<?> clazz = Class.forName(classFullPath);
                if(clazz.isAnnotationPresent(Controller.class)){
                    //这里就直接默认beanName是类名的首字母小写
                    String beanName = StringUtils.uncapitalize(clazz.getSimpleName());
                    ioc.put(beanName,clazz.newInstance());
                } else if (clazz.isAnnotationPresent(Service.class)) {
                    //先获取到Service的value值
                    Service serviceAnnotation = clazz.getAnnotation(Service.class);
                    String beanName = serviceAnnotation.value();
                    Object o = clazz.newInstance();
                    if ("".equals(beanName)) {//没有指定value，默认使用接口/类名首字母小写的
                        Class<?>[] interfaces = clazz.getInterfaces();
                        //如果有多个接口，那么使用多个接口名来注入
                        for (Class<?> i : interfaces) {
                            String beanName2 = StringUtils.uncapitalize(i.getSimpleName());
                            ioc.put(beanName2,o);
                        }
                        //类名
                        String beanName3 = StringUtils.uncapitalize(clazz.getSimpleName());
                        ioc.put(beanName3,o);
                    }else {
                        ioc.put(beanName,o);
                    }

                } //如果有其他注解可以扩展
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //编写方法，完成属性的自动装配
    public void executeAutoWired(){
        //判断ioc有没有要装配的对象
        if(ioc.isEmpty()){
            return;
        }
        //遍历ioc容器所有bean对象，然后获取到bean的所有字段/属性，判断是否需要
        for(Map.Entry<String,Object>entry:ioc.entrySet()){
            String key=entry.getKey();
            Object bean=entry.getValue();
            Class<?> clazz=bean.getClass();
            for(Field field:clazz.getDeclaredFields()){
                if (field.isAnnotationPresent(AutoWired.class)){
                    String beanName = field.getAnnotation(AutoWired.class).value();
                    if ("".equals(beanName)) {
                        //没有设置value，按照字段类型的首字母小写作为名字
                        beanName=StringUtils.uncapitalize(field.getType().getSimpleName());
                    }
                    if(null==ioc.get(beanName)){
                        //说明要装配的bean不在ioc中
                        throw new RuntimeException("ioc容器中，不存在要装配的bean");
                    }
                    field.setAccessible(true);//防止属性是private，要爆破一下
                    //现在可以装配属性了
                    try {
                        field.set(bean,ioc.get(beanName));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
